package org.ttitfly.ttcore.interceptor;

import static org.springside.modules.utils.WebConstants.AJAX_CURRENT_PAGE;
import static org.springside.modules.utils.WebConstants.DIR;
import static org.springside.modules.utils.WebConstants.PAGE;
import static org.springside.modules.utils.WebConstants.POPUP_NAMESPACE;
import static org.springside.modules.utils.WebConstants.REGEX;
import static org.springside.modules.utils.WebConstants.SORT;

import java.lang.reflect.Field;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ttitfly.ttcore.util.annotation.Ajax;
import org.ttitfly.ttcore.util.annotation.IsSearchObject;
import org.ttitfly.ttcore.util.annotation.PageSize;
import org.ttitfly.ttcore.util.exception.PagingException;
import org.ttitfly.ttcore.util.helper.PropertyHelper;
import org.ttitfly.ttcore.util.log.LogMessageUtil;
import org.ttitfly.ttcore.util.query.Pagable;
import org.ttitfly.ttcore.util.query.SearchObject;
import org.ttitfly.ttcore.util.query.Sort;
import org.ttitfly.ttcore.util.query.Sortable;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class PagingInterceptor extends AbstractInterceptor {

    private static final long serialVersionUID = 1L;

    private static final Log logger = LogFactory.getLog(PagingInterceptor.class);

    private Map<String, String[]> parameters;

    @SuppressWarnings("unchecked")
    @Override
    public String intercept(ActionInvocation ai) throws Exception {

        ActionContext context = ai.getInvocationContext();
        parameters = context.getParameters();

        Object action = ai.getAction();
        Field soField = getSearchFeild(action.getClass());

        Map<String, Object> session = context.getSession();
        if (null != soField) {

            String namespace = ai.getProxy().getNamespace();
            String actionName = ai.getProxy().getActionName();
            Object obj = PropertyHelper.getValue(action, soField.getName());
            if (obj == null) {
                Class cls = soField.getType();
                try {
                    obj = cls.newInstance();
                    Object sessionSO = session.get(namespace + "_" + actionName + "_" + cls.getName());
                    if (!namespace.equals(POPUP_NAMESPACE) && sessionSO != null
                            && sessionSO.getClass().getName().equals(cls.getName())) {
                        obj = sessionSO;
                        ((SearchObject) obj).setFirstAccess(Boolean.FALSE);
                    } else {
                        ((SearchObject) obj).setPageSize(getPageSize(action));
                        ((SearchObject) obj).setFirstAccess(Boolean.TRUE);
                    }
                } catch (Exception e) {
                    logger.error(LogMessageUtil.printStack(e));
                    throw new PagingException(e);
                }
                ((SearchObject) obj).setExportFull(isExportFull());
                ((SearchObject) obj).setExportLimit(getExportLimit(action));
            }
            if (obj instanceof Pagable) {
                ((Pagable) obj).setCurrentPage(getCurrentPage(action));
                ((Pagable) obj).setExportFull(isExportFull());
                //new
                ((SearchObject) obj).setPageSize(getPageSize(action));
            }
            if (obj instanceof Sortable) {
                Sort sort = new Sort();
                sort.setSortKey(getSort());
                sort.setSortDir(getDir());
                if (sort.getSortKey() != null) {
                    ((Sortable) obj).getSorts().clear();
                }
                ((Sortable) obj).addSort(sort);
            }
            try {
                PropertyHelper.setValue(action, soField.getName(), obj);
            } catch (Exception e) {
                logger.error(LogMessageUtil.printStack(e));
                throw new PagingException(e);
            }
            if (!namespace.equals(POPUP_NAMESPACE)) {
                session.put(namespace + "_" + actionName + "_" + obj.getClass().getName(), obj);
            }
        }

        String result = ai.invoke();

        return result;
    }

    private int getPageSize(Object action) {
        for (Field soField : action.getClass().getDeclaredFields()) {
            if (soField.isAnnotationPresent(PageSize.class)) {
                return soField.getAnnotation(PageSize.class).value();
            }
        }

        return PageSize.SIZE;
    }

    private int getExportLimit(Object action) {
        for (Field soField : action.getClass().getDeclaredFields()) {
            if (soField.isAnnotationPresent(PageSize.class)) {
                return soField.getAnnotation(PageSize.class).exportLimit();
            }
        }

        return PageSize.EXPORT_LIMIT;
    }

    private int getCurrentPage(Object action) {
        int page = 1;
        if (action.getClass().isAnnotationPresent(Ajax.class)) {
            if (parameters.get(AJAX_CURRENT_PAGE) != null) {
                page = Integer.parseInt((parameters.get(AJAX_CURRENT_PAGE))[0]);
            }
            return page >= 1 ? page : 1;
        }
        if (parameters.get(PAGE) != null) {
            page = Integer.parseInt((parameters.get(PAGE))[0]);
        } else if (parameters.get("start") != null) {
            Integer start = Integer.parseInt(parameters.get("start")[0]);
            page = (start / getPageSize(action)) + 1;
        }
        return page >= 1 ? page : 1;
    }

    private boolean isExportFull() {
        boolean export = false;
        for (String o : parameters.keySet()) {
            if (o.matches(REGEX)) {
                if (parameters.get(o) != null) {
                    export = true;
                }
            }
        }
        return export;
    }

    private String getSort() {
        String sort = null;
        if (parameters.get(SORT) != null) {
            sort = (parameters.get(SORT))[0];
        }
        return sort;
    }

    private String getDir() {
        String dir = null;
        if (parameters.get(DIR) != null) {
            dir = (parameters.get(DIR))[0];
        }
        return dir;
    }

    private Field getSearchFeild(Class<?> clz) {
        for (Field soField : clz.getDeclaredFields()) {
            if (soField.isAnnotationPresent(IsSearchObject.class)) {
                return soField;
            }
        }

        if (clz.getSuperclass() != null) {
            return getSearchFeild(clz.getSuperclass());
        }

        return null;
    }

}
